#include <iostream>
#include <functional>
#include <string>
#include <unordered_map>
#include "Util/Sock.hpp"
#include "Util/Epoll.hpp"
#include "Util/Protocol.hpp"

using namespace std;
using namespace ns_protocol;

class connection;
class TcpServer;

#define ALIVE_TIME 30 // 连接多久不访问，关闭连接(s)

using func_t = function<void(connection *)>;                    // 回调方法
using service_t = function<void(connection *, const string &)>; // 业务逻辑

class connection // 监听套接字和获取上来的连接都需要通过connection进行管理
{
public:
    connection(int sock) : _sock(sock), _tsvr(nullptr)
    {
    }
    void SetCallBack(func_t recv_cb, func_t send_cb, func_t except_cb) // 为该连接设置回调函数
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

public:
    int _sock; // 套接字

    string _inBuffer;  // 接收缓冲区
    string _outBuffer; // 发送缓冲区

    func_t _recv_cb;   // 读取回调
    func_t _send_cb;   // 发送回调
    func_t _except_cb; // 异常回调

    TcpServer *_tsvr; // 回指指针

    unsigned long _last_time; // 用于连接管理
};

class TcpServer // 基于epoll多路转接和tcp协议的服务器
{
public:
    static const uint16_t gport = 8080;
    static const int g_revs_num = 64; // 就绪数组的长度：一次最多获取多少就绪事件
    TcpServer(uint16_t port = gport) : _port(port)
    {
        // 1.创建套接字
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);

        // 2.创建epoll模型
        _poll.EpollCreate();

        // 3.监听套接字也被作为连接，进行管理
        AddConnection(_listensock, bind(&TcpServer::Accepter, this, placeholders::_1) /*读取回调：获取连接*/, nullptr, nullptr);

        // 4.初始化就绪数组
        _revs = new struct epoll_event[g_revs_num];

        logMessage(DEBUG, "init TcpServer success\n");
    }
    void AddConnection(int sock, func_t recv_cb, func_t send_cb, func_t except_cb) // 创建连接对象，并进行管理
    {
        // 1.epoll以ET模式工作，要求所有读写都是非阻塞(多次)
        Sock::SetNonBlock(sock);

        // 2.构建连接对象，便于服务器管理
        connection *conn = new connection(sock);
        conn->SetCallBack(recv_cb, send_cb, except_cb);
        conn->_tsvr = this;
        conn->_last_time = time(NULL); // 建立连接时间戳

        // 3.对于多路转接服务器，一般只添加读取事件，写入和异常事件按需添加
        _poll.AddSockToEpoll(sock, EPOLLIN | EPOLLET); // 边缘触发

        // 4.将对象放入连接集合中管理起来：建立sock与conneion*的映射
        connections.insert(make_pair(sock, conn));
    }
    void Accepter(connection *conn)
    {
        while (true) // 可能同时来了多个连接:非阻塞多次读取
        {
            // 1.获取新连接
            string cli_ip;
            uint16_t cli_port;
            int accept_errno = 0; // 带出错误码errno
            int sock = Sock::Accept(_listensock, &cli_ip, &cli_port, &accept_errno);
            bool err = false;
            if (sock < 0)
            {
                // 获取连接失败，通过错误码判断：1.出错  2.连接获取完了
                if (accept_errno == EWOULDBLOCK || accept_errno == EAGAIN) // 连接已获取完毕
                    break;
                else if (accept_errno == EINTR) // 被信号打断(概率极低)
                    continue;
                else // 出错了
                {
                    logMessage(ERROR, "accept error -> errno:%d, errstring:%s\n", accept_errno, strerror(accept_errno));
                    err = true;
                    break;
                }
            }
            logMessage(DEBUG, "new connection:[%d]%s:%d\n", sock, cli_ip.c_str(), cli_port);

            // 2.将sock新连接管理起来，设置对应的三个回调方法
            if (!err)
                AddConnection(sock, bind(&TcpServer::Recver, this, placeholders::_1),
                              bind(&TcpServer::Sender, this, placeholders::_1),
                              bind(&TcpServer::Excepter, this, placeholders::_1));
        }
    }
    void Recver(connection *conn)
    {
        int sock = conn->_sock;
        conn->_last_time = time(NULL); // 更新连接的访问时间
        bool err_flag = false;         // 错误标记
        while (true)                   // 非阻塞读,将数据全都读到该连接维护的inbuffer里
        {
            char buffer[1024];
            ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
            // 这样读取，不能保证读到一个完整报文，需要定制应用层协议
            if (s > 0)
            {
                buffer[s] = 0;
                conn->_inBuffer += buffer; // 读到数据直接拼接到连接各自的输入缓冲区里
            }
            else if (s == 0) // 客户端关闭,断开连接:也是一种IO，也会被epoll检测到
            {
                conn->_except_cb(conn);
                logMessage(NORMAL, "client[%d] quit\n", sock);
                err_flag = true;
                break;
            }
            else // 出错 or 读完了
            {
                if (errno == EWOULDBLOCK || errno == EAGAIN)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    conn->_except_cb(conn); // 处理异常
                    logMessage(WARNING, "recv error -> errno:%d, errstring:%s\n", errno, strerror(errno));
                    err_flag = true;
                    break;
                }
            }
        }
        if (!err_flag)
        {
            // logMessage(NORMAL, "%d:%s\n", sock, conn->_inBuffer.c_str());

            // 协议定制：拆分出完整报文，交给上层业务逻辑
            vector<string> Requests;
            SpliteRequest(conn->_inBuffer, &Requests); // 分割出n个完整报文(n >= 0)
            for (auto &Request : Requests)             // 将请求依次业务处理
            {
                _service(conn, Request);
            }
        }
    }
    void Sender(connection *conn)
    {
        int sock = conn->_sock;
        string &outBuffer = conn->_outBuffer;
        while (true) // 非阻塞多次写
        {
            ssize_t s = send(sock, outBuffer.c_str(), outBuffer.size(), 0); // 非阻塞
            if (s > 0)
            {
                outBuffer.erase(0, s); // 写过的内容从发送缓冲区删去（用户级），如果丢包，传输层会自动重传
                if (outBuffer.empty())
                    break; // 写完了
            }
            else
            {
                if (errno == EWOULDBLOCK || errno == EAGAIN)
                    break; // 写事件不满足：可能是对方接收窗口为0
                else if (errno == EINTR)
                    continue;
                else // 出错
                {
                    conn->_except_cb(conn);
                    logMessage(WARNING, "send error -> errno:%d, errstring:%s\n", errno, strerror(errno));
                    break;
                }
            }
        }
        if (!outBuffer.empty())
            EnableReanWrite(conn, true, true); // 没写完，继续关心写事件
        else
            EnableReanWrite(conn, true, false); // 写完，去掉写事件关心
    }
    void Excepter(connection *conn) // 将异常连接关闭
    {
        // 0.检测对象是否存在:不存在，说明在上一个事件处理时被异常处理过了
        if (!ConnectionIsExist(conn->_sock))
            return;
        // 1.去掉关心
        _poll.DelSockFromEpoll(conn->_sock);
        // 2.去掉连接对象
        connections.erase(conn->_sock);
        // 3.关闭连接
        close(conn->_sock);
        // 4.释放对象
        delete conn;
    }
    void Start(service_t service)
    {
        _service = service; // 上层要执行的业务逻辑
        while (true)
        {
            connectAliveCheck();                              // 检测连接是否活跃，释放不活跃连接
            int n = _poll.EpollWait(_revs, g_revs_num, 5000); // 获取一批就绪事件
            switch (n)
            {
            case 0: // 超时
                logMessage(DEBUG, "%s\n", "epoll timeout");
                break;
            case -1: // epoll出错
                logMessage(WARNING, "%s\n", "epoll error");
                break;
            default: // 获取到新连接/新请求
                handleEvent(n);
                break;
            }
        }
    }
    void handleEvent(int n)
    {
        for (int i = 0; i < n; i++) // 处理就绪事件（n个）
        {
            int sock = _revs[i].data.fd;
            uint32_t event = _revs[i].events;

            connection *conn = connections[sock];

            // 统一异常处理：出错了直接调用读写出错，自动释放
            if (event & EPOLLERR)
                event = (EPOLLIN | EPOLLOUT);
            if (event & EPOLLHUP)
                event = (EPOLLIN | EPOLLOUT);

            if (ConnectionIsExist(sock) && ((event & EPOLLIN) && conn->_recv_cb)) // 读事件就绪
            {
                conn->_recv_cb(conn);
            }
            if (ConnectionIsExist(sock) && ((event & EPOLLOUT) && conn->_send_cb)) // 写事件就绪
            {
                conn->_send_cb(conn);
            }

            // 判断连接是否存在：连接不存在，说明可能已经被异常回收了
            // 例如：同时触发读写事件，其中读异常，被回收了，再处理写，要判断连接是否还存在
        }
    }
    bool ConnectionIsExist(int sock) // 判断该连接是否存在
    {
        auto iter = connections.find(sock);
        if (iter == connections.end())
            return false;
        return true;
    }
    void EnableReanWrite(connection *conn, bool Readable, bool Writable) // 打开连接的读写关心
    {
        uint32_t events = ((Readable ? EPOLLIN : 0) | (Writable ? EPOLLOUT : 0)); // 读写事件标记位
        _poll.EpollCtl(conn->_sock, events);
    }
    void connectAliveCheck()
    {
        vector<connection *> closeConn; // 存放待释放连接，防止迭代器失效问题
        for (auto &iter : connections)
        {
            connection *conn = iter.second;
            if (conn->_sock == _listensock || conn->_sock == _poll.getEpollfd()) // 不用检测listensock和epfd
                continue;
            if (conn->_last_time + ALIVE_TIME < time(NULL)) // 连接长时间不访问服务器
            {
                closeConn.push_back(conn);
            }
        }
        for (auto &conn : closeConn)
        {
            logMessage(NORMAL, "connection not alive, close:%d\n", conn->_sock);
            conn->_except_cb(conn);
        }
    }
    ~TcpServer()
    {
        if (_listensock > 0)
            close(_listensock);
        if (_revs)
            delete[] _revs;
        for (auto conn : connections)
        {
            delete conn.second;
        }
    }

private:
    int _listensock;
    uint16_t _port;

    Epoll _poll;                                  // epoll类
    unordered_map<int, connection *> connections; // 连接集合
    struct epoll_event *_revs;                    // 存放就绪事件

    service_t _service; // 业务逻辑
};